package Dungeon;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;

import be.kuleuven.cs.som.annotate.Basic;

import Coordinates.Axis;
import Coordinates.Coordinates;
import Predicate.Predicate;
import Square.Square;

public class CompositeDungeon<T extends Square> extends Dungeon<T> {

	/**
	 * Creates a new composite dungeon
	 * 
	 * @param 	xDimension
	 * 			The x-dimension for the new dungeon
	 * 
	 * @param 	yDimension
	 * 			The y-dimension for the new dungeon
	 * 
	 * @param 	zDimension
	 * 			The z-dimension for the new dungeon
	 * 
	 * @effect 	This CompositeDungeon is intialized as a new Dungeon with given x,y,z-dimensions
	 * 			| super(xDimension, yDimension, zDimension)
	 */
	public CompositeDungeon(long xDimension, long yDimension, long zDimension)
			throws IllegalArgumentException {
		super(xDimension, yDimension, zDimension);
	}
	
	/**
	 * Checks if the given origin is valid for the current dungeon
	 * 
	 * @param 	xOrigin
	 * 			The x-coordinate of the origin
	 * @param 	yOrigin
	 * 			The y-coordinate of the origin
	 * @param 	zOrigin
	 * 			The z-coordinate of the origin
	 * @return	True if the given origin is valid, false if it isn't
	 * 			| if(xOrigin < 0 || yOrigin < 0 || zOrigin < 0)
	 *			|	result == false
	 *			| if(xOrigin > getDungeonDimensions(Axis.X_AXIS) || yOrigin > getDungeonDimensions(Axis.Y_AXIS) || zOrigin > getDungeonDimensions(Axis.Z_AXIS))
	 *			|	result == false
	 *			| result == true
	 */
	public boolean isValidOrigin(long xOrigin, long yOrigin, long zOrigin)
	{
		if(xOrigin < 0 || yOrigin < 0 || zOrigin < 0)
			return false;
		if(xOrigin > getDungeonDimensions(Axis.X_AXIS) || yOrigin > getDungeonDimensions(Axis.Y_AXIS) || zOrigin > getDungeonDimensions(Axis.Z_AXIS))
			return false;
		return true;
	}
	
	
	/**
	 * 
	 * Checks if the given sub-dungeon can be added to this dungeon at given origin coordinates
	 * 
	 * @param 	dungeon
	 * 			The sub-dungeon to add to this dungeon
	 * 
	 * @param 	xOrigin
	 * 			The x-coordinate of the origin
	 * 
	 * @param 	yOrigin
	 * 			The y-coordinate of the origin
	 * 
	 * @param 	zOrigin
	 * 			The z-coordinate of the origin
	 * 
	 * @return	False if the sub-dungeon's dimensions fall out of the parent dungeon's dimensions
	 * 			|	if( xOrigin + dungeon.getDungeonDimensions(Axis.X_AXIS) > getDungeonDimensions(Axis.X_AXIS)
	 * 			|    	|| yOrigin + dungeon.getDungeonDimensions(Axis.Y_AXIS) > getDungeonDimensions(Axis.Y_AXIS)
	 * 			|	   	|| zOrigin + dungeon.getDungeonDimensions(Axis.Z_AXIS) > dungeon.getDungeonDimensions(Axis.Z_AXIS))
	 *			|	result == false
	 * 
	 * @return  False if the sub-dungeon overlaps with one of the current dungeons
	 * 			| for each origin, value in dungeons
	 * 			| 		 if(!(xOrigin + dungeon.getDungeonDimensions(Axis.X_AXIS) <= origin.getX()
	 *			|		    || origin.getX() + value.getDungeonDimensions(Axis.X_AXIS)<= xOrigin 
	 *			|		    || zOrigin + dungeon.getDungeonDimensions(Axis.Z_AXIS) <= origin.getZ()
	 *			|		    || origin.getZ() + value.getDungeonDimensions(Axis.Z_AXIS)<= zOrigin 
	 *			|		    || yOrigin + dungeon.getDungeonDimensions(Axis.Y_AXIS) <= origin.getY()
	 *			|		    || origin.getY() + value.getDungeonDimensions(Axis.Y_AXIS) <= yOrigin))
	 *			|	    	result == false
	 * 
	 * @return	True if all other cases
	 * 			| result == true
	 */
	public boolean canAddDungeon(Dungeon<? extends T> dungeon, long xOrigin, long yOrigin, long zOrigin)
	{
		if( xOrigin + dungeon.getDungeonDimensions(Axis.X_AXIS) > getDungeonDimensions(Axis.X_AXIS)
		    	|| yOrigin + dungeon.getDungeonDimensions(Axis.Y_AXIS) > getDungeonDimensions(Axis.Y_AXIS)
		    	|| zOrigin + dungeon.getDungeonDimensions(Axis.Z_AXIS) > getDungeonDimensions(Axis.Z_AXIS))
		{
			return false; 
		}
	
		for (Entry<Coordinates, Dungeon<? extends T>> entry : dungeons.entrySet()) 
		{
		    Coordinates origin = entry.getKey();
		    Dungeon<?> value = entry.getValue();
		    if(!(xOrigin + dungeon.getDungeonDimensions(Axis.X_AXIS) <= origin.getX()
			    || origin.getX() + value.getDungeonDimensions(Axis.X_AXIS)<= xOrigin 
			    || zOrigin + dungeon.getDungeonDimensions(Axis.Z_AXIS) <= origin.getZ()
			    || origin.getZ() + value.getDungeonDimensions(Axis.Z_AXIS)<= zOrigin 
			    || yOrigin + dungeon.getDungeonDimensions(Axis.Y_AXIS) <= origin.getY()
			    || origin.getY() + value.getDungeonDimensions(Axis.Y_AXIS) <= yOrigin)) 
		    	return false;
		}
		
		return true;
	}
	
	/**
	 * Returns a dungeon at given origin coordinates
	 * 
	 * @param 	xOrigin
	 * 			The x-coordinate of the origin
	 * @param 	yOrigin
	 * 			The y-coordinate of the origin
	 * @param 	zOrigin
	 * 			The z-coordinate of the origin
	 * @throws 	IllegalArgumentException
	 * 			The dungeon does not have a sub dungeon at the given coordinates
	 * 			| ! hasDungeonAtCoordinate(xOrigin, yOrigin, zOrigin)
	 */
	
	@Basic
	public Dungeon<? extends T> getDungeonAtOrigin(long xOrigin, long yOrigin, long zOrigin)
	{
		if(!hasDungeonAtCoordinate(xOrigin, yOrigin, zOrigin))
			throw new IllegalArgumentException();
		else
			return dungeons.get(new Coordinates(xOrigin, yOrigin, zOrigin));
	}
	
	/**
	 * 
	 * @param 	dungeon
	 * 			The sub-dungeon to add to this dungeon
	 * @param 	xOrigin
	 * 			The x-coordinate of the origin
	 * @param 	yOrigin
	 * 			The y-coordinate of the origin
	 * @param 	zOrigin
	 * 			The z-coordinate of the origin
	 * @throws	IllegalArgumentException
	 * 			The given dungeon is a nullpointer
	 * 			| (dungeon == null)
	 * @throws 	IllegalArgumentException
	 * 			The given origin coordinates aren't valid
	 * 			| !isValidOrigin(xOrigin, yOrigin, zOrigin)
	 * @throws 	IllegalArgumentException
	 * 			The dungeon already contains the given sub-dungeon
	 * 			| hasDungeon(dungeon)
	 * @throws 	IllegalArgumentException
	 * 			The given dungeon does not fit in this dungeon or it overlaps with other dungeons
	 * 			| !canAddDungeon(dungeon, xOrigin, yOrigin, zOrigin)
	 * @throws 	IllegalArgumentException
	 * 			The given dungeon already has another parent dungeon
	 * 			| dungeon.getParentDungeon() != null
	 * 
	 * @effect	loops through all the squares in the bottom and the ceiling of the dungeon and merges them if
	 * 			they touch another square from another dungeon.
	 * 			| checkBottomCeilingSquares(Dungeon<? extends T> dungeon
	 * 	
	 * @effect	loops through all the squares in the left and the right  of the dungeon and merges them if 
	 * 			they touch another square from another dungeon.
	 * 			| checkLeftRightSquares(Dungeon<? extends T> dungeon
	 * 
	 * @effect	loops through all the squares in the front and the back  of the dungeon and merges them if 
	 * 			they touch another square from another dungeon.
	 * 			| checkFrontBackSquares(Dungeon<? extends T> dungeon
	 */
	public void addDungeon(Dungeon<? extends T> dungeon, long xOrigin, long yOrigin, long zOrigin) throws IllegalArgumentException{
		if (dungeon == null)
			throw new IllegalArgumentException();
		if(!isValidOrigin(xOrigin, yOrigin, zOrigin))
			throw new IllegalArgumentException();
		if(hasDungeon(dungeon))
			throw new IllegalArgumentException();
		if(!canAddDungeon(dungeon, xOrigin, yOrigin, zOrigin))
			throw new IllegalArgumentException();
		if(dungeon.getParentDungeon() != null)
			throw new IllegalArgumentException();
		dungeons.put(new Coordinates(xOrigin, yOrigin, zOrigin), dungeon);
		dungeon.setParentDungeon(this);
		checkFrontBackSquares(dungeon);
		checkLeftRightSquares(dungeon);
		checkBottomCeilingSquares(dungeon);
		
	}
	
	/**
	 * Method that loops through all the squares in the bottom and the ceiling of the dungeon.
	 * 
	 * @param 	dungeon
	 * 			Dungeon that is being looped through
	 * 
	 * @post 	The borders of the touching squares are merged.
	 * 			origin = dungeon.getDistanceToRootOrigin();
	 *			| root = dungeon.getRootDungeon();
	 *			| for (x = 0; x < dungeon.getDungeonDimensions(Axis.X_AXIS); x++){
	 *			|	for(y = 0; y < dungeon.getDungeonDimensions(Axis.Y_AXIS); y++){
	 *			|	z = 0;
	 *			|	if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x, origin.getY()+y, origin.getZ()+z-1) != null )
	 *			|		dungeon.getSquare(x, y, z).getBorder(Border.Direction.FLOOR) == root.getSquare(origin.getX()+x , origin.getY()+y, origin.getZ()+z-1).getBorder(Border.Direction.CEILING)
	 *			|	z = dungeon.getDungeonDimensions(Axis.Z_AXIS) - 1;
	 *			|	if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x, origin.getY()+y, origin.getZ()+z+1) != null )
	 *			|		dungeon.getSquare(x, y, z).getBorder(Border.Direction.CEILING) == root.getSquare(origin.getX()+x, origin.getY()+y, origin.getZ()+z+1).getBorder(Border.Direction.FLOOR)
	 */
	private void checkBottomCeilingSquares(Dungeon<? extends T> dungeon) {
		long x,y,z;
		Coordinates origin = dungeon.getDistanceToRootOrigin();
		CompositeDungeon<?> root = dungeon.getRootDungeon();
		for (x = 0; x < dungeon.getDungeonDimensions(Axis.X_AXIS); x++){
			for(y = 0; y < dungeon.getDungeonDimensions(Axis.Y_AXIS); y++){
				z = 0;
				if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x, origin.getY()+y, origin.getZ()+z-1) != null )
					dungeon.getSquare(x, y, z).mergeBorders(root.getSquare(origin.getX()+x , origin.getY()+y, origin.getZ()+z-1), Border.Direction.FLOOR);
				z = dungeon.getDungeonDimensions(Axis.Z_AXIS) - 1;
				if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x, origin.getY()+y, origin.getZ()+z+1) != null )
					dungeon.getSquare(x, y, z).mergeBorders(root.getSquare(origin.getX()+x, origin.getY()+y, origin.getZ()+z+1), Border.Direction.CEILING);
			}
		}
	}

	
	/**
	 * Method that loops through all the squares in the left and the right of the dungeon.
	 * 
	 * @param 	dungeon
	 * 			Dungeon that is being looped through
	 * 
	 * @post 	The borders of the touching squares are merged.
	 * 			origin = dungeon.getDistanceToRootOrigin();
	 *			| root = dungeon.getRootDungeon();
	 *			| for (y = 0; y < dungeon.getDungeonDimensions(Axis.Y_AXIS); y++){
	 *			|	for(z = 0; z < dungeon.getDungeonDimensions(Axis.Z_AXIS); z++){
	 *			|	z = 0;
	 *			|	if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x - 1, origin.getY()+y, origin.getZ()+z) != null )
	 *			|		dungeon.getSquare(x, y, z).getBorder(Border.Direction.WEST) == root.getSquare(origin.getX()+x - 1, origin.getY()+y, origin.getZ()+z).getBorder(Border.Direction.EAST)
	 *			|	z = dungeon.getDungeonDimensions(Axis.Z_AXIS) - 1;
	 *			|	if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x+1, origin.getY()+y, origin.getZ()+z) != null )
	 *			|		dungeon.getSquare(x, y, z).getBorder(Border.Direction.EAST) == root.getSquare(origin.getX()+x + 1, origin.getY()+y, origin.getZ()+z).getBorder(Border.Direction.WEST)
	 */
	private void checkLeftRightSquares(Dungeon<? extends T> dungeon) {
		long x,y,z;
		Coordinates origin = dungeon.getDistanceToRootOrigin();
		CompositeDungeon<?> root = dungeon.getRootDungeon();
		for(y = 0; y < dungeon.getDungeonDimensions(Axis.Y_AXIS); y++){
			for(z = 0; z < dungeon.getDungeonDimensions(Axis.Z_AXIS); z++){
				x = 0;
				if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x - 1, origin.getY()+y, origin.getZ()+z) != null )
					dungeon.getSquare(x, y, z).mergeBorders(root.getSquare(origin.getX()+x - 1, origin.getY()+y, origin.getZ()+z), Border.Direction.WEST);
				x = dungeon.getDungeonDimensions(Axis.X_AXIS);
				if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x+1, origin.getY()+y, origin.getZ()+z) != null )
					dungeon.getSquare(x, y, z).mergeBorders(root.getSquare(origin.getX()+x + 1, origin.getY()+y, origin.getZ()+z), Border.Direction.EAST);
			}
		}
	}
	/**
	 * Method that loops through all the squares in the front and the back of the dungeon.
	 * 
	 * @param 	dungeon
	 * 			Dungeon that is being looped through
	 * 
	 * @post 	The borders of the touching squares are merged.
	 * 			origin = dungeon.getDistanceToRootOrigin();
	 *			| root = dungeon.getRootDungeon();
	 *			| for (x = 0; x < dungeon.getDungeonDimensions(Axis.Y_AXIS); x++){
	 *			|	for(z = 0; z < dungeon.getDungeonDimensions(Axis.Z_AXIS); z++){
	 *			|	z = 0;
	 *			|	if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x, origin.getY()+y-1, origin.getZ()+z) != null )
	 *			|		dungeon.getSquare(x, y, z).getBorder(Border.Direction.WEST) == root.getSquare(origin.getX()+x, origin.getY()+y-1, origin.getZ()+z).getBorder(Border.Direction.EAST)
	 *			|	z = dungeon.getDungeonDimensions(Axis.Z_AXIS) - 1;
	 *			|	if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x+1, origin.getY()+y, origin.getZ()+z) != null )
	 *			|		dungeon.getSquare(x, y, z).getBorder(Border.Direction.EAST) == root.getSquare(origin.getX()+x , origin.getY()+y+1, origin.getZ()+z).getBorder(Border.Direction.WEST)
	 */
	private void checkFrontBackSquares(Dungeon<? extends T> dungeon) {
		long x,y,z;
		Coordinates origin = dungeon.getDistanceToRootOrigin();
		CompositeDungeon<?> root = dungeon.getRootDungeon();
		y = 0;
		for(x = 0; x < dungeon.getDungeonDimensions(Axis.X_AXIS); x++){
			for(z = 0; z < dungeon.getDungeonDimensions(Axis.Z_AXIS); z++){
				y = 0;
				if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x, origin.getY()+y-1, origin.getZ()+z) != null )
					dungeon.getSquare(x, y, z).mergeBorders(root.getSquare(origin.getX()+x, origin.getY()+y-1, origin.getZ()+z), Border.Direction.NORTH);
				y = dungeon.getDungeonDimensions(Axis.Y_AXIS) - 1;
				if( dungeon.getSquare(x, y, z) != null && root.getSquare(origin.getX()+x, origin.getY()+y+1, origin.getZ()+z) != null )
					dungeon.getSquare(x, y, z).mergeBorders(root.getSquare(origin.getX()+x , origin.getY()+y+1, origin.getZ()+z), Border.Direction.SOUTH);
			}	
		}
	}
	
	/**
	 * Removes the dungeon that has an origin at given coordinates
	 * @param 	xOrigin
	 * 			The x-coordinate of the origin
	 * @param 	yOrigin
	 * 			The y-coordinate of the origin
	 * @param 	zOrigin
	 * 			The z-coordinate of the origin
	 * @throws 	IllegalArgumentException
	 * 			The dungeon does not have a sub dungeon at the given coordinates
	 * 			| ! hasDungeonAtCoordinate(xOrigin, yOrigin, zOrigin)
	 */
	public void removeDungeonAt (long xOrigin, long yOrigin, long zOrigin) throws IllegalArgumentException
	{
		if(!hasDungeonAtCoordinate(xOrigin, yOrigin, zOrigin))
			throw new IllegalArgumentException();
		else
			dungeons.remove(new Coordinates(xOrigin, yOrigin, zOrigin));
	}
	
	/**
	 * 
	 * Checks if a given sub-dungeon is a member if this dungeon
	 * 
	 * @param 	dungeon
	 * 		    The dungeon to check for
	 * 
	 * @return	True if the given sub-dungeon is a member of the current parent dungeon, false if it isn't
	 */
	public boolean hasDungeon(Dungeon<? extends T> dungeon)
	{
		return dungeons.containsValue(dungeon);
	}

	/**
	 * Method that gets the origin from the given dungeon and returns the coordinates
	 * 
	 * @param		dungeon
	 * 				given dungeon that is going to be checked
	 * 
	 * @pre			This composite dungeon contains the given dungeon
	 * 				| hasDungeon(dungeon)
	 * 
	 * @return		Returns the coordinates for the Origin
	 * 				| for each entry : dungeon.entrySet()
	 * 				|	origin = entry.getKey()
	 * 				|	value = entry.getValue()
	 * 				|	if(value == dungeon)
	 * 				|		result == origin
	 */
	public Coordinates getOriginFromDungeon(Dungeon<? extends T> dungeon)
	{
		assert this.hasDungeon(dungeon);
		
		for (Entry<Coordinates, Dungeon<? extends T>> entry : dungeons.entrySet()) 
		{
		    Coordinates origin = entry.getKey();
		    Dungeon<?> value = entry.getValue();
		    if(value == dungeon)
		    	return origin;
		}
		
		assert false;
		return null;
	}
	
	/**
	 * Checks if there's a dungeon at the given coordinates
	 * 
	 * @param 	xOrigin
	 * 			The x-coordinate of the origin
	 * 
	 * @param 	yOrigin
	 * 			The y-coordinate of the origin
	 * 
	 * @param 	zOrigin
	 * 			The z-coordinate of the origin
	 * 
	 * @return	True if there's a dungeon at given coordinates, false if there isn't 
	 * 			| result == dungeon.containsKeys(Coordinates(xOrigin, yOrigin, zOrigin))
	 */
	public boolean hasDungeonAtCoordinate(long xOrigin, long yOrigin, long zOrigin)
	{
		return dungeons.containsKey(new Coordinates(xOrigin, yOrigin, zOrigin));
	}
	
	/**
	 * A variable containing this dungeon's sub-dungeons
	 */
	private HashMap<Coordinates, Dungeon<? extends T>> dungeons = new HashMap<Coordinates, Dungeon<? extends T>>();

	/**
	 * Returns the square from the given coordinates in this dungeon.
	 * 
	 * @param 	x
	 * 			X-Coordinate to get the square from.
	 * 
	 * @param 	y
	 * 			Y-Coordinate to get the square from.
	 * 
	 * @param 	z
	 * 			Z-Coordinate to get the square from.
	 * 
	 * @return	Returns the square at the position x,y,z
	 * 			| for each entry in dungeons.entrySet()
	 * 			|	 if( origin.getX() <= x && origin.getX()+value.getDungeonDimensions(Axis.X_AXIS) > x
	 *	    	|		&& origin.getY() <= y && origin.getY()+value.getDungeonDimensions(Axis.Y_AXIS) > y
	 *	    	|		&& origin.getZ() <= z && origin.getZ()+value.getDungeonDimensions(Axis.Z_AXIS) > z )
	 *			| 			result == value.getSquare(x - origin.getX() , y - origin.getY(), z - origin.getZ())
	 *
	 *@return	Returns null if there's no square at given coordinates
	 *			| result == null:
	 */
	@Override
	public T getSquare(long x, long y, long z) {
		for (Entry<Coordinates, Dungeon<? extends T>> entry : dungeons.entrySet()) 
		{
		    Coordinates origin = entry.getKey();
		    Dungeon<? extends T> value = entry.getValue();
		    if(    origin.getX() <= x && origin.getX()+value.getDungeonDimensions(Axis.X_AXIS) > x
		    	&& origin.getY() <= y && origin.getY()+value.getDungeonDimensions(Axis.Y_AXIS) > y
		    	&& origin.getZ() <= z && origin.getZ()+value.getDungeonDimensions(Axis.Z_AXIS) > z)
		    {
		    	
		    	return value.getSquare(x - origin.getX() , y - origin.getY(), z - origin.getZ());
		    }
		}
		return null;
	}
	
	/**
	 * Returns an iterator 
	 * 
	 * @return	Returns an iterator with all squares from this dungeon
	 * 			| result == getSquaresCollection().iterator()
	 */
	@Override
	public Iterator<T> getIterator()
	{
		return getSquaresCollection().iterator();
	}
	
	/**
	 * Returns a filtered iterator
	 * 
	 * @param 	predicate
	 * 			A logical predicate used to filter the squares in this dungeon
	 * @return	Returns a filtered iterator with the squares from this dungeon
	 * 			| squaresToRemove = new ArrayList<T>()
	 *			| collection = getSquaresCollecttion()
	 *   		| for each element in collection
	 *       	|	if (!predicate.apply(element))
	 *       	|		squaresToRemove.add(element)
	 *   		| collection.removeAll(squaresToRemove)
	 *   		| result ==  collection.iterator()
	 */
	@Override
	public Iterator<T> getIterator(Predicate<T> predicate)
	{
		Collection<T> squaresToRemove = new ArrayList<T>();
		Collection<T> collection = getSquaresCollection();
	    for (T element: collection)
	        if (!predicate.apply(element))
	        	squaresToRemove.add(element);
	    collection.removeAll(squaresToRemove);
	    return collection.iterator();
	}
	
	/**
	 * Returns a collection with all the squares of the dungeons that this CompositeDungeon contains
	 * 
	 * @return	A collection with all the squares of the dungeons that this CompositeDungeon contains
	 * 			| collections = new ArrayList<T>()
	 *			| for each dungeon in dungeons
	 *			| {
	 *			|	if(dungeon instanceof RegularDungeon)
	 *			|		collections.addAll( dungeon.getSquaresMap().values() )
	 *			|	else if(dungeon instanceof CompositeDungeon)
	 *			|		collections.addAll( dungeon.getSquaresCollection() )
	 *			| }
	 *			| result == collections
	 */
	public Collection<T> getSquaresCollection()
	{
		Collection<T> collections = new ArrayList<T>();

		for(Dungeon<? extends T> dungeon : dungeons.values())
		{
			if(dungeon instanceof RegularDungeon)
				collections.addAll(((RegularDungeon<? extends T>)dungeon).getSquaresMap().values());
			else if(dungeon instanceof CompositeDungeon)
				collections.addAll( ((CompositeDungeon<? extends T>)dungeon).getSquaresCollection() );
		}
		return collections;
	}
	
	
	
}
